﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Configuration;
using System.IO;
using System.Linq;
using MetaCollection.ContentService.Configuration;
using MetaCollection.ContentService.Http;
using MetaCollection.ContentService.Template;
using MetaCollection.Framework;
using MetaCollection.Framework.Model;

namespace MetaCollection.ContentService.WebDav.Methods
{
	/// <summary>
	/// 
	/// This handler should implement the partial request byte range request returning 206 and 416 (range not satisfable) codes as appropriate.
	/// 
	/// A request will contain a Range header like so
	/// 
	/// Range: bytes=0-500
	/// Range: bytes=0-500,900-1000
	/// Range: bytes=0-
	/// 
	/// An appropriate response to a range request is
	/// 
	/// HTTP/1.1 206 Partial content
	/// Date: Wed, 15 Nov 1995 06:25:24 GMT
	/// Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
	/// Content-Range: bytes 21010-47021/47022
	/// Content-Length: 26012
	/// Content-Type: image/gif 
	/// </summary> 
	public static class Get
	{
		/// <summary>
		/// The Default Document (appended to the end of folder requests)
		/// </summary>
		private static string DefaultDocument;
		/// <summary>
		/// Item Templating Configuration and methods
		/// </summary>
		private static ItemTemplate Templating = new ItemTemplate();

		/// <summary>
		/// Responds to a request.
		/// </summary>
		/// <param name="context">The context.</param>
		public static void Respond(DavContext context, IRequestHandler handler)
		{
			IItemInstance item = context.Item as IItemInstance;

			if (item == null)
				throw new Http.ClientException(Http.ClientErrorStatus.NotFound);

			if (!context.Request.IsModifiedSinceLastRequest(item))
			{
				context.Response.SetStatus((int)RedirectStatus.NotModified, "Not Modified");
				return;
			}
			// For GET requests we need to redirect to the default document when requests target a folder (exclusions apply see method for details)
			if (item.MimeType == "application/x-folder")
				UpdateFolderDefaultDocument(context, handler, ref item);

			Uri templateUri = null;
			string itemContentType = item.MimeType;

			if (Templating.IsTemplateEnabled(context, item, ref itemContentType, ref templateUri))
				context.Response.OutputText(ItemTemplate.RenderItemWithTemplate(context, handler, item, templateUri), itemContentType);
			else
				context.Response.OutputBinary(ItemBinaryReader(context, handler, item.Href, item.Version), itemContentType);
		}

		/// <summary>
		/// Initializes the <see cref="Get"/> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Loading configuration data must be done in the constructor.")]
		static Get()
		{
			ContentConfigurationSection config = (ContentConfigurationSection)ConfigurationManager.GetSection("contentConfiguration");
			DefaultDocument = config.DefaultDocument;
		}

		/// <summary>
		/// Updates the folder path with the default document as needed.
		/// If the request is a folder and the folder has a default docuement specified, we will return that
		/// document (the default document may be the folder), otherwise we  will attempt to return the default
		/// document(from config file) from that folder.
		/// </summary>
		/// <param name="requestItem">The request item.</param>
		/// <remarks>TODO: check for folderlist override parameter /edit mode?</remarks>
		private static void UpdateFolderDefaultDocument(DavContext context, IRequestHandler handler, ref IItemInstance requestItem)
		{
			IItemInstance initialItem = requestItem;
			IItemProperty property = null;
			if (requestItem.Properties != null)
			{
				property = requestItem.Properties.FirstOrDefault(p => p.KeyName == "ContentService.DefaultDocument");
			}
			if (property != null)
			{
				if (!property.KeyValue.Equals(requestItem.Name)) // if the names match the user wants the folder returned (for directory listing?), otherwise append default name
					requestItem.Href = new Uri(Path.Combine(requestItem.Href.AbsoluteUri, property.KeyValue));
			}
			else
			{
				requestItem.Href = new Uri(Path.Combine(requestItem.Href.AbsoluteUri, DefaultDocument));
			}
			requestItem = handler.ItemRequest(context.User, RequestTypes.GetItem, requestItem.Href).FirstOrDefault();

			if (requestItem == null)
				requestItem = initialItem; // No default document and it's a folder: give back the folder item itself
		}


		/// <summary>
		/// Returns a stream for the item binary
		/// </summary>
		/// <param name="itemUri">The item URI.</param>
		private static Stream ItemBinaryReader(DavContext context, IRequestHandler handler, Uri itemUri, int version)
		{
			var itemOutputStream = handler.ResourceRequest(context.User, RequestTypes.GetResource | RequestTypes.VersionSpecific, itemUri, version);
			
			if (itemOutputStream == null || !itemOutputStream.CanRead)
				return Stream.Null;
			else
				return itemOutputStream;
		}
	}
}
